Howl Audio Context
Introducción
En esta documentación explicaremos cómo utilizar Howler.js junto con React mediante un Context personalizado para manejar la reproducción y configuración de audio en tu aplicación. El objetivo es proporcionar una forma sencilla de reproducir, pausar, detener y controlar volúmenes de diferentes tipos de audio (acción, escena y voz) sin tener que replicar lógica en cada componente.
Tabla de Contenidos
- Introducción
- Tabla de Contenidos
- Dependencias
- Constantes y Configuración Inicial
- Contexto (
AudioHowlContext
) - Proveedor (
AudioHowlProvider
) - Hook Personalizado (
useAudioHowl
) - Ejemplo de Uso
- Descargar el Paquete
Dependencias
Para poder utilizar esta implementación necesitas:
- React (≥16.8)
- Howler.js
- Un bundler o sistema de empaquetado que permita importar archivos de audio (por ejemplo, Webpack o Vite).
Instalación rápida de Howler.js:
npm install howler
# o
yarn add howler
Constantes y Configuración Inicial
Antes de entrar a detalle con el contexto y el proveedor, definimos algunas constantes que servirán como base para el estado y la configuración de volúmenes por defecto:
// Constantes y configuración
export const AUDIO_STATUS = {
STOP: "stop",
START: "start",
PAUSE: "pause",
RESUME: "resume",
NONE: "n/a"
};
// Estado de carga de audio
export const LOAD_STATUS = {
UNLOADED: "unloaded",
LOADING: "loading",
READY: "ready",
ERROR: "error"
};
export const AUDIO_TYPES = {
ACTION: "action",
SCENE: "scene",
VOICE: "voice"
};
export const DEFAULT_VOLUMES = {
[AUDIO_TYPES.ACTION]: 0.3,
[AUDIO_TYPES.SCENE]: 0.03,
[AUDIO_TYPES.VOICE]: 0.85
};
Explicación Detallada
-
AUDIO_STATUS
- Define los diferentes estados que puede tener la reproducción de un audio.
"stop"
: el audio está detenido."start"
: el audio se acaba de iniciar."pause"
: el audio está en pausa."resume"
: el audio se está reanudando."n/a"
: estado inicial o sin asignar.
-
LOAD_STATUS
- Controla el estado de carga de cada archivo de audio.
"unloaded"
: aún no se ha iniciado la carga."loading"
: el archivo se está cargando."ready"
: el archivo está cargado y listo para reproducir."error"
: hubo un error al cargar el archivo.
-
AUDIO_TYPES
-
Permite diferenciar los distintos tipos de audio en la aplicación:
ACTION
→ sonidos de acciones puntuales (por ejemplo, efectos).SCENE
→ música o ambiente de fondo.VOICE
→ narraciones o diálogos.
-
-
DEFAULT_VOLUMES
- Establece un volumen por defecto para cada tipo de audio.
- Si el usuario no especifica un volumen distinto al reproducir, se usará el valor aquí definido.
Contexto (AudioHowlContext
)
El contexto es el punto central que proporciona, mediante un Provider, todos los métodos y estados necesarios para controlar el audio de forma global.
import { createContext } from 'react';
import { LOAD_STATUS, AUDIO_TYPES } from './AudioHowlConst';
/**
* @typedef {Object} AudioController
* @property {number} volume - Volumen por defecto para este controlador
* @property {Object|null} howlInstance - Instancia de Howl que gestiona este audio
* @property {string} status - Estado actual de reproducción (basado en AUDIO_STATUS)
* @property {string} loadStatus - Estado actual de carga (basado en LOAD_STATUS)
* @property {string|null} currentSrc - Fuente actual del audio
*/
/**
* @typedef {Object} AudioConfigParams
* @property {boolean} [loop=false] - Indica si el audio debe reproducirse en bucle
* @property {number|null} [volume=null] - Volumen específico para esta reproducción
*/
/**
* @typedef {Object} PlayParams
* @property {string} type - Tipo de audio a reproducir (use AUDIO_TYPES)
* @property {string} audio - URL o recurso del audio a reproducir
* @property {AudioConfigParams} [configAudio] - Configuración de reproducción
*/
/**
* @typedef {Object} VolumeParams
* @property {string} type - Tipo de audio a ajustar (use AUDIO_TYPES)
* @property {number} volume - Nivel de volumen (0.0 a 1.0)
*/
/**
* @typedef {Object} PreloadParams
* @property {string} type - Tipo de audio a precargar (use AUDIO_TYPES)
* @property {string} audio - URL o recurso del audio a precargar
* @property {AudioConfigParams} [configAudio] - Configuración del audio
*/
// Valores por defecto para el contexto
const defaultContext = {
/**
* Reproduce un audio según el tipo especificado
* @param {PlayParams} params - Parámetros para reproducir el audio
* @returns {void}
*/
play: ({ type, audio, configAudio }) => { },
/**
* Detiene la reproducción de un tipo específico de audio
* @param {string} type - Tipo de audio a detener (use AUDIO_TYPES)
* @returns {void}
*/
stop: (type) => { },
/**
* Alterna entre pausa y reproducción para un tipo específico de audio
* @param {string} type - Tipo de audio a detener (use AUDIO_TYPES)
* @returns {void}
*/
togglePause: (type) => { },
/**
* Alterna el estado de silencio para todos los tipos de audio
* @returns {void}
*/
mute: () => { },
/**
* Establece el volumen para un tipo específico de audio
* @param {VolumeParams} params - Parámetros para ajustar el volumen
* @returns {void}
*/
setVolume: ({ type, volume }) => { },
/**
* Precarga un audio sin reproducirlo, para tenerlo disponible inmediatamente cuando se necesite
* @param {PreloadParams} params - Parámetros para precargar el audio
* @returns {string} Estado de carga actual
*/
preloadAudio: ({ type, audio, configAudio }) => LOAD_STATUS.UNLOADED,
/**
* Verifica si un audio específico ya está cargado y listo para reproducirse
* @param {string} type - Tipo de audio a verificar (use AUDIO_TYPES)
* @param {string} audioSrc - URL o recurso del audio a verificar
* @returns {boolean} true si el audio está cargado y listo
*/
isAudioLoaded: (type, audioSrc) => false,
/**
* Estado actual de carga para cada tipo de audio
* @type {Object.<string, string>}
*/
loadingStates: {
[AUDIO_TYPES.ACTION]: LOAD_STATUS.UNLOADED,
[AUDIO_TYPES.SCENE]: LOAD_STATUS.UNLOADED,
[AUDIO_TYPES.VOICE]: LOAD_STATUS.UNLOADED
},
/**
* Tipos de audio disponibles (ACTION, SCENE, VOICE)
* @type {Object.<string, string>}
*/
audioTypes: AUDIO_TYPES,
/**
* Constantes para estados de carga (UNLOADED, LOADING, READY, ERROR)
* @type {Object.<string, string>}
*/
loadStatus: LOAD_STATUS,
/**
* Controladores de audio para cada tipo
* @type {Object.<string, AudioController>}
*/
controllers: {
[AUDIO_TYPES.ACTION]: null,
[AUDIO_TYPES.SCENE]: null,
[AUDIO_TYPES.VOICE]: null
}
};
/**
* Contexto para la gestión de audio con Howler.js
* Proporciona métodos para reproducir, pausar, detener y controlar audio.
*
* @example
* // Ejemplo de uso básico
* const { play, stop, audioTypes } = useContext(AudioHowlContext);
*
* // Reproducir audio de fondo
* play({
* type: audioTypes.SCENE,
* audio: '/path/to/music.mp3',
* configAudio: { loop: true, volume: 0.5 }
* });
*
* // Detener audio
* stop(audioTypes.SCENE);
*/
const AudioHowlContext = createContext(defaultContext);
export default AudioHowlContext;
Explicación del Contexto
-
Tipos de Parámetros (
@typedef
)-
Se definen varios typedefs para documentar la forma de los objetos que usaremos:
AudioController
: almacena la instancia Howl, estado y volumen de cada canal.AudioConfigParams
: opciones de configuración al reproducir (comoloop
yvolume
).PlayParams
,VolumeParams
,PreloadParams
: describen los parámetros que reciben las funciones del contexto.
-
-
defaultContext
- Contiene las funciones y estados predeterminados que tendrá el contexto antes de que el proveedor los reemplace con su implementación real.
- Funciones vacías (
play
,stop
,togglePause
, etc.) que sirven como placeholders. - Valores de
loadingStates
inicializados en"unloaded"
. - Un objeto
controllers
inicial connull
para cada tipo de audio, que luego será reemplazado por un controlador real en el proveedor.
-
AudioHowlContext
- Es el contexto creado mediante
createContext(defaultContext)
. - Cualquier componente que haga
useContext(AudioHowlContext)
tendrá acceso a estas funciones y estados.
- Es el contexto creado mediante
Proveedor (AudioHowlProvider
)
El proveedor es donde se implementa la lógica real de gestión de audio usando Howler.js. A continuación detallamos su estructura y funcionamiento.
import React, { useCallback, useRef, useState } from 'react';
import { Howl } from 'howler';
import AudioHowlContext from './AudioHowlContext';
import { AUDIO_STATUS, AUDIO_TYPES, DEFAULT_VOLUMES, LOAD_STATUS } from './AudioHowlConst';
// Activación de audio en evento de click (para navegadores que lo requieren)
let isActiveAudio = false;
document.addEventListener('click', function () {
if (isActiveAudio) return;
console.log(`%c Audio permission granted`, `background: #222; color: #f6bfaf; font-size: 12px; padding: 5px;`);
isActiveAudio = true;
});
// Funciones auxiliares
const createEmptyController = (volume = 0) => ({
volume,
howlInstance: null,
status: AUDIO_STATUS.NONE,
loadStatus: LOAD_STATUS.UNLOADED,
currentSrc: null
});
const getEffectiveVolume = (requestedVolume, defaultVolume) => {
return requestedVolume ?? defaultVolume;
};
// Componente principal
const AudioHowlProvider = ({ children }) => {
// Estado de audio
const audioControllers = useRef({
[AUDIO_TYPES.ACTION]: createEmptyController(DEFAULT_VOLUMES[AUDIO_TYPES.ACTION]),
[AUDIO_TYPES.SCENE]: createEmptyController(DEFAULT_VOLUMES[AUDIO_TYPES.SCENE]),
[AUDIO_TYPES.VOICE]: createEmptyController(DEFAULT_VOLUMES[AUDIO_TYPES.VOICE])
});
// Estado para notificar cambios en la carga de audio
const [loadingStates, setLoadingStates] = useState({
[AUDIO_TYPES.ACTION]: LOAD_STATUS.UNLOADED,
[AUDIO_TYPES.SCENE]: LOAD_STATUS.UNLOADED,
[AUDIO_TYPES.VOICE]: LOAD_STATUS.UNLOADED
});
const isMuted = useRef(false);
// Verificar existencia de controlador de audio
const getActiveController = (type) => {
if (!type || !audioControllers.current[type]) return null;
const controller = audioControllers.current[type];
if (!controller.howlInstance) return null;
return controller;
};
// Actualizar estado de carga de un tipo de audio
const updateLoadStatus = (type, status) => {
if (!type || !audioControllers.current[type]) return;
audioControllers.current[type].loadStatus = status;
setLoadingStates(prevStates => ({
...prevStates,
[type]: status
}));
};
// Crear instancia de Howl con manejo de eventos de carga
const createHowlInstance = useCallback(({ type, src, loop = false, volume = 0 }) => {
if (!src || !type) return null;
updateLoadStatus(type, LOAD_STATUS.LOADING);
const instance = new Howl({
src: [src],
loop,
volume,
preload: true,
html5: false // Usar métodos Web Audio API para mejor rendimiento y control
});
// Eventos de carga
instance.once('load', () => {
updateLoadStatus(type, LOAD_STATUS.READY);
// console.log(`Audio ${type} loaded and ready`);
});
instance.once('loaderror', () => {
updateLoadStatus(type, LOAD_STATUS.ERROR);
// console.error(`Error loading audio ${type}`);
});
return instance;
}, []);
// Verificar si un audio ya está cargado con la misma fuente
const isAudioLoaded = useCallback((type, audioSrc) => {
if (!type || !audioControllers.current[type]) return false;
const controller = audioControllers.current[type];
return (
controller.howlInstance &&
controller.currentSrc === audioSrc &&
controller.loadStatus === LOAD_STATUS.READY
);
}, []);
// Reproducir audio
const play = useCallback(({
type,
audio,
configAudio = { loop: false, volume: null },
}) => {
if (!audio || !type || !audioControllers.current[type]) return;
const controller = audioControllers.current[type];
const { loop, volume } = configAudio;
const effectiveVolume = getEffectiveVolume(volume, controller.volume);
// Si el audio ya está cargado con la misma fuente
if (isAudioLoaded(type, audio)) {
if(controller.howlInstance.playing()) return
controller.status = AUDIO_STATUS.START;
controller.howlInstance.volume(effectiveVolume);
controller.howlInstance.mute(isMuted.current);
controller.howlInstance.play();
return;
}
// Actualizar estado
controller.status = AUDIO_STATUS.START;
// Limpiar instancia anterior
if (controller.howlInstance) {
controller.howlInstance.unload();
}
// Guardar referencia de la fuente actual
controller.currentSrc = audio;
// Crear y configurar nueva instancia
controller.howlInstance = createHowlInstance({
type,
src: audio,
loop,
volume: effectiveVolume
});
if (controller.howlInstance) {
controller.howlInstance.mute(isMuted.current);
controller.howlInstance.play();
}
}, [createHowlInstance, isAudioLoaded]);
// Precargar audio sin reproducirlo
const preloadAudio = useCallback(({
type,
audio,
configAudio = { loop: false, volume: null }
}) => {
if (!audio || !type || !audioControllers.current[type]) return;
// Si ya está cargado, no hacer nada
if (isAudioLoaded(type, audio)) {
return;
}
const controller = audioControllers.current[type];
const { loop, volume } = configAudio;
const effectiveVolume = getEffectiveVolume(volume, controller.volume);
// Limpiar instancia anterior
if (controller.howlInstance) {
controller.howlInstance.unload();
}
// Guardar referencia de la fuente actual
controller.currentSrc = audio;
// Crear instancia para precargar
controller.howlInstance = createHowlInstance({
type,
src: audio,
loop,
volume: effectiveVolume
});
return controller.loadStatus;
}, [createHowlInstance, isAudioLoaded]);
// Detener audio
const stop = useCallback((type) => {
const controller = getActiveController(type);
if (!controller) return;
controller.status = AUDIO_STATUS.STOP;
controller.howlInstance.stop();
}, []);
// Pausar/reanudar audio
const togglePause = useCallback((type) => {
const controller = getActiveController(type);
if (!controller) return;
if (![AUDIO_STATUS.STOP, AUDIO_STATUS.PAUSE].includes(controller.status)) {
controller.status = AUDIO_STATUS.PAUSE;
controller.howlInstance.pause();
} else {
controller.status = AUDIO_STATUS.RESUME;
controller.howlInstance.play();
}
}, []);
// Silenciar/activar sonido
const toggleMute = useCallback(() => {
const newMuteState = !isMuted.current;
isMuted.current = newMuteState;
Object.values(audioControllers.current).forEach(controller => {
if (controller.howlInstance) {
controller.howlInstance.mute(newMuteState);
}
});
}, []);
// Ajustar volumen
const setVolume = useCallback(({ type, volume }) => {
const controller = getActiveController(type);
if (!controller || volume === undefined) return;
const effectiveVolume = getEffectiveVolume(volume, controller.volume);
controller.howlInstance.volume(effectiveVolume);
}, []);
// API contexto
const contextValue = {
play,
stop,
togglePause,
mute: toggleMute,
setVolume,
preloadAudio,
isAudioLoaded,
loadingStates,
audioTypes: AUDIO_TYPES,
loadStatus: LOAD_STATUS,
controllers: audioControllers.current
};
return (
<AudioHowlContext.Provider value={contextValue}>
{children}
</AudioHowlContext.Provider>
);
};
export default AudioHowlProvider;
Explicación del Proveedor
-
Activación del Audio en Navegadores Modernos
- Algunos navegadores exigen un evento de interacción previa (por ejemplo, un
click
) para permitir la reproducción de audio. - Se escucha un
click
global y, la primera vez que suceda, se marcaisActiveAudio = true
.
- Algunos navegadores exigen un evento de interacción previa (por ejemplo, un
-
Funciones Auxiliares
createEmptyController(volume)
: crea un objeto controlador con estado inicial (sin instancia Howl, estadoNONE
, cargaUNLOADED
y volumen pasado por parámetro).getEffectiveVolume(requestedVolume, defaultVolume)
: devuelve el volumen que se debe usar—si el usuario no especificó uno, usa el por defecto.
-
Estados Internos
audioControllers
(medianteuseRef
) contiene, para cada tipo de audio, un controlador construido concreateEmptyController
.loadingStates
(medianteuseState
) guarda únicamente el estado de carga de cada tipo para forzar re-render cuando cambie.isMuted
(medianteuseRef
) indica si actualmente todos los audios están silenciados.
-
Métodos Principales
-
getActiveController(type)
: devuelve el controlador activo para el tipo especificado (onull
si no existe o no tiene instancia Howl). -
updateLoadStatus(type, status)
: cambia el estado de carga en el controlador y actualizaloadingStates
para reflejarlo en la UI. -
createHowlInstance({ type, src, loop, volume })
: crea una nueva instanciaHowl
con la configuración indicada, registra eventosload
yloaderror
, y devuelve la instancia. -
isAudioLoaded(type, audioSrc)
: comprueba si el controlador de ese tipo ya tiene cargada la misma fuente (currentSrc === audioSrc
) y su estado de carga esREADY
. -
play({ type, audio, configAudio })
:-
Si el audio ya está cargado (misma fuente y estado
READY
), revisa si no se esté reproduciendo; si no, ajusta volumen, mute y lo reproduce. -
Si no está cargado o es una fuente distinta:
- Cambia estado a
START
. - Si había una instancia previa, la descarga con
.unload()
. - Almacena la nueva fuente en
currentSrc
. - Genera la instancia con
createHowlInstance
. - Aplica estado
mute
y llama a.play()
.
- Cambia estado a
-
-
preloadAudio({ type, audio, configAudio })
:- Si ya está cargado, no hace nada.
- Descarga instancia previa (si existía), guarda la nueva fuente, crea instancia (sin llamar a
.play()
). - Devuelve el estado de carga actual (que al llamar a
createHowlInstance
pasará deLOADING
aREADY
oERROR
).
-
stop(type)
: detiene la reproducción y cambiastatus
aSTOP
. -
togglePause(type)
: si el audio está reproduciéndose, lo pausa (cambia aPAUSE
); si está enSTOP
oPAUSE
, lo reanuda (cambia aRESUME
). -
toggleMute()
: invierte el estado deisMuted
y lo aplica a todas las instancias Howl de los controladores existentes. -
setVolume({ type, volume })
: ajusta el volumen de la instancia Howl del tipo dado, usandogetEffectiveVolume
para determinar el valor final.
-
-
Valor del Contexto (
contextValue
)- Agrupa todas las funciones (
play
,stop
,togglePause
,mute
,setVolume
,preloadAudio
,isAudioLoaded
) y estados (loadingStates
,audioTypes
,loadStatus
,controllers
) que estarán disponibles para cualquier componente que consuma el contexto.
- Agrupa todas las funciones (
Hook Personalizado (useAudioHowl
)
Para simplificar el acceso al contexto en los componentes, se define un hook que verifica que se encuentre dentro de un AudioHowlProvider
:
import { useContext } from 'react';
import AudioHowlContext from './AudioHowlContext';
// Hook personalizado para consumir el contexto
const useAudioHowl = () => {
const context = useContext(AudioHowlContext);
if (!context) {
throw new Error('useAudioHowl debe usarse dentro de un AudioHowlProvider');
}
return context;
};
export default useAudioHowl;
Explicación
useAudioHowl
: simplemente llama auseContext(AudioHowlContext)
.- Si se invoca fuera de un proveedor, arroja un error para advertir al desarrollador que el contexto no está disponible.
Ejemplo de Uso
A continuación vemos un componente de ejemplo (SubTestAudio
) que demuestra un flujo típico de carga y reproducción de audio:
import React, { useEffect, useState } from 'react';
import useAudioHowl from '@lib/audioHowl/useAudioHowl';
import audio from '@assets/audio/testScene.mp3';
import audio2 from '@assets/audio/testScene2.mp3';
import sourceAudio from '@assets/audio/testSound.wav';
const SubTestAudio = () => {
const {
loadingStates,
loadStatus,
audioTypes,
play,
preloadAudio,
isAudioLoaded,
mute,
togglePause,
stop,
setVolume
} = useAudioHowl();
const [sceneAudioState, setSceneAudioState] = useState('No cargado');
// Cargar audio principal al montar el componente
useEffect(() => {
play({ type: audioTypes.SCENE, audio, configAudio: { loop: true } });
}, [play, audioTypes]);
// Monitorear estados de carga
useEffect(() => {
// Escuchar cambios en el estado de carga del audio "scene"
const currentLoadState = loadingStates[audioTypes.SCENE];
switch (currentLoadState) {
case loadStatus.LOADING:
setSceneAudioState('Cargando...');
break;
case loadStatus.READY:
setSceneAudioState('Listo para reproducir');
break;
case loadStatus.ERROR:
setSceneAudioState('Error al cargar');
break;
default:
setSceneAudioState('No cargado');
}
}, [loadingStates, audioTypes, loadStatus]);
// Precarga del audio2 para tenerlo disponible rápidamente
useEffect(() => {
// Precargar el segundo audio sin reproducirlo
preloadAudio({
type: audioTypes.ACTION,
audio: sourceAudio,
configAudio: { volume: 1 }
});
}, [preloadAudio, audioTypes]);
const handlePlaySecondAudio = () => {
// Comprobar si ya está cargado antes de reproducir
if (isAudioLoaded(audioTypes.ACTION, sourceAudio)) {
// "¡Audio ya cargado! Reproduciendo inmediatamente";
} else {
// "Cargando audio antes de reproducir...";
}
play({
audio: sourceAudio,
type: audioTypes.ACTION,
configAudio: { volume: 1 }
});
};
return (
<div className="fixed z-20 w-full bottom-10 left-0">
<div className='w-full h-full bg-amber-200 flex flex-col justify-center items-center gap-2'>
<div className='text-center mb-4'>
Estado del audio: <strong>{sceneAudioState}</strong>
</div>
<div className='flex justify-center items-center gap-2'>
<div onClick={() => togglePause(audioTypes.SCENE)} className='w-20 h-20 bg-red-400 flex items-center justify-center cursor-pointer'>Pausa</div>
<div onClick={() => mute()} className='w-20 h-20 bg-red-400 flex items-center justify-center cursor-pointer'>Mute</div>
<div onClick={() => stop(audioTypes.SCENE)} className='w-20 h-20 bg-red-400 flex items-center justify-center cursor-pointer'>Stop</div>
<div onClick={() => setVolume({ type: audioTypes.SCENE, volume: 1 })} className='w-20 h-20 bg-red-400 flex items-center justify-center cursor-pointer'>Volumen</div>
<div onClick={handlePlaySecondAudio} className='w-20 h-20 bg-red-400 flex items-center justify-center cursor-pointer'>Play Audio</div>
<div onClick={() => play({ audio: audio2, type: audioTypes.SCENE, configAudio: { loop: false, volume: 0.5 } })} className='w-20 h-20 bg-red-400 flex items-center justify-center cursor-pointer'>Play scene</div>
</div>
</div>
</div>
);
};
export default SubTestAudio;
Desglose del Componente
-
Importaciones
- Se importa el hook
useAudioHowl
para acceder a las funciones de reproducción y estado de carga. - Se importan tres archivos de audio (
testScene.mp3
,testScene2.mp3
ytestSound.wav
) desde la carpeta de assets.
- Se importa el hook
-
Desestructuración del Hook
const {
loadingStates,
loadStatus,
audioTypes,
play,
preloadAudio,
isAudioLoaded,
mute,
togglePause,
stop,
setVolume
} = useAudioHowl();loadingStates
: objeto con el estado de carga para cada tipo de audio.loadStatus
: constantes para comparar (LOADING
,READY
,ERROR
, etc.).audioTypes
: constantes de tipo de audio (ACTION
,SCENE
,VOICE
).play
,preloadAudio
,isAudioLoaded
,mute
,togglePause
,stop
,setVolume
: funciones para controlar el audio.
-
Estado Local (
sceneAudioState
)- Almacena un texto descriptivo del estado de carga del audio de escena.
- Inicialmente
"No cargado"
.
-
Primer
useEffect
– Reproducción AutomáticauseEffect(() => {
play({ type: audioTypes.SCENE, audio, configAudio: { loop: true } });
}, [play, audioTypes]);- Al montarse el componente, llama a
play
para iniciar la reproducción del audio de tipoSCENE
en bucle.
- Al montarse el componente, llama a
-
Segundo
useEffect
– Monitoreo de CargauseEffect(() => {
const currentLoadState = loadingStates[audioTypes.SCENE];
switch (currentLoadState) {
case loadStatus.LOADING:
setSceneAudioState('Cargando...');
break;
case loadStatus.READY:
setSceneAudioState('Listo para reproducir');
break;
case loadStatus.ERROR:
setSceneAudioState('Error al cargar');
break;
default:
setSceneAudioState('No cargado');
}
}, [loadingStates, audioTypes, loadStatus]);- Escucha los cambios en
loadingStates[SCENE]
para actualizar el texto que indica si el audio está cargando, listo o con error.
- Escucha los cambios en
-
Tercer
useEffect
– Precarga de Audio de AcciónuseEffect(() => {
preloadAudio({
type: audioTypes.ACTION,
audio: sourceAudio,
configAudio: { volume: 1 }
});
}, [preloadAudio, audioTypes]);- Precarga
testSound.wav
para que, cuando se llame a reproducción (por ejemplo, al presionar un botón), no haya latencia de carga.
- Precarga
-
Función
handlePlaySecondAudio
const handlePlaySecondAudio = () => {
if (isAudioLoaded(audioTypes.ACTION, sourceAudio)) {
// Audio ya cargado ⇒ Reproducción inmediata
} else {
// Mostrar mensaje de carga
}
play({
audio: sourceAudio,
type: audioTypes.ACTION,
configAudio: { volume: 1 }
});
};- Comprueba si el audio de acción ya está cargado (
isAudioLoaded
). - Llama a
play
independientemente, pero si no estaba cargado, se mostrará “Cargando...” antes de reproducir.
- Comprueba si el audio de acción ya está cargado (
-
Renderizado de la Interfaz
-
Un
div
fijo en la parte inferior de la pantalla con botones para:- Pausar/reanudar (
togglePause
). - Silenciar/activar todo (
mute
). - Detener audio de escena (
stop
). - Ajustar volumen al máximo (
setVolume
). - Reproducir el segundo audio (
handlePlaySecondAudio
). - Reproducir otro audio de escena (
play({ audio2 })
).
- Pausar/reanudar (
-
Descargar el Paquete
Si quieres utilizar esta implementación en tu proyecto, puedes descargar el ZIP con el código completo desde:
Dentro del ZIP encontrarás la siguiente estructura:
LibAudioHowl/
├── src/
│ ├── AudioHowlConst.js
│ ├── AudioHowlContext.js
│ ├── AudioHowlProvider.js
│ ├── useAudioHowl.js
│ └── components/
│ └── SubTestAudio.jsx
├── package.json
└── README.md
AudioHowlConst.js
: Define las constantes (AUDIO_STATUS
,LOAD_STATUS
,AUDIO_TYPES
,DEFAULT_VOLUMES
).AudioHowlContext.js
: Contiene el contexto y typedefs para documentar los parámetros.AudioHowlProvider.js
: Implementa la lógica de Howler.js y expone la API al contexto.useAudioHowl.js
: Hook personalizado para acceder fácilmente al contexto.components/SubTestAudio.jsx
: Componente de prueba que muestra un ejemplo de uso.
y agrega al inicio de tu APP
import './App.css'
import { RouterApp } from "./router";
import AudioHowlProvider from '@lib/audioHowl/AudioHowlProvider';
function App() {
return (
<AudioHowlProvider>
<RouterApp />
</AudioHowlProvider>
)
}
export default App